package logger

import (
	"container/list"
	"fmt"
	"giot/pkg/util"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

// MyLogger 日志
type MyLogger struct {
	logLevelMap map[string]string
	logRoot     string
	showLog     bool
	showOther   bool
	showOther2  bool
	before      int
	mutex       sync.Mutex

	catch *list.List

	inited bool
}

type logStruct struct {
	logLevel string
	logValue string
}

const (
	logDebug   = "DEBUG"
	logInfo    = "INFO"
	logWarring = "WARRING"
	logError   = "ERROR"
	logOther   = "OTHER"
	logOther2  = "OTHER2"
)

// Debug 调试日志
func (l *MyLogger) Debug(value string) {
	if _, ok := l.logLevelMap[logDebug]; ok == true {
		l.writeLog(logDebug, value)
	}
}

// Debugf 格式化输出
func (l *MyLogger) Debugf(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logDebug]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.Debug(str)
	}
}

// Info 信息日志
func (l *MyLogger) Info(value string) {
	if _, ok := l.logLevelMap[logInfo]; ok == true {
		l.writeLog(logInfo, value)
	}
}

// Infof 格式化输出
func (l *MyLogger) Infof(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logInfo]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.Info(str)
	}
}

// Error 错误日志
func (l *MyLogger) Error(value string) {
	if _, ok := l.logLevelMap[logError]; ok == true {
		trace := GetStackTrace(3)
		info := fmt.Sprintf("%v堆栈\n%v", value, trace)
		l.writeLog(logError, info)
	}
}

// Errorf 格式化输出
func (l *MyLogger) Errorf(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logError]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.Error(str)
	}
}

// ErrorX 输出错误日志
func (l *MyLogger) ErrorX(e error) {
	if _, ok := l.logLevelMap[logError]; ok == true {
		str := fmt.Sprintf("%v", e)
		l.Error(str)
	}
}

// Warring  警告日志
func (l *MyLogger) Warring(value string) {
	if _, ok := l.logLevelMap[logWarring]; ok == true {
		l.writeLog(logWarring, value)
	}
}

// Warringf 格式化输出
func (l *MyLogger) Warringf(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logWarring]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.Warring(str)
	}
}

// Otherf 格式化输出
func (l *MyLogger) Otherf(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logOther]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.writeLog(logOther, str)
	}
}

// Otherf2 格式化输出
func (l *MyLogger) Otherf2(formatter string, value ...interface{}) {
	if _, ok := l.logLevelMap[logOther2]; ok == true {
		str := fmt.Sprintf(formatter, value...)
		l.writeLog(logOther2, str)
	}
}

// Other  其他日志
func (l *MyLogger) Other(value string) {
	if _, ok := l.logLevelMap[logOther]; ok == true {
		l.writeLog(logOther, value)
	}
}

func (l *MyLogger) InitLog(file string) {
	l.readConfig(file)
	l.inited = true
}

func (l *MyLogger) readConfig(filePath string) {
	configInfo, err := util.ReadConfigFile(filePath)
	var loglevel string
	l.logLevelMap = make(map[string]string, 4)
	if nil != err {
		loglevel = logInfo
		l.showLog = true
		if len(filePath) != 0 {
			println("读配置文件错误,错误信息:%v", err.Error())
		}
		configInfo = nil
	} else {
		loglevel = logInfo
		l.showLog = true
		l.showOther = false
		l.showOther2 = false

		if v, ok := configInfo["logger.loglevel"]; ok && v != nil {
			loglevel = v.(string)
		}
		if v, ok := configInfo["logger.showlog"]; ok && v != nil {
			l.showLog = v.(bool)
		}
		if v, ok := configInfo["logger.showother"]; ok && v != nil {
			l.showOther = v.(bool)
		}
		if v, ok := configInfo["logger.showother2"]; ok && v != nil {
			l.showOther2 = v.(bool)
		}
	}
	loglevel = strings.ToUpper(loglevel)
	l.logLevelMap[logDebug] = logDebug
	l.logLevelMap[logInfo] = logInfo
	l.logLevelMap[logWarring] = logWarring
	l.logLevelMap[logError] = logError
	l.logLevelMap[logOther2] = logOther2
	if l.showOther {
		l.logLevelMap[logOther] = logOther
	}

	if loglevel == logInfo {
		delete(l.logLevelMap, logDebug)
	} else if loglevel == logWarring {
		delete(l.logLevelMap, logDebug)
		delete(l.logLevelMap, logInfo)
	} else if loglevel == logError {
		delete(l.logLevelMap, logDebug)
		delete(l.logLevelMap, logInfo)
		delete(l.logLevelMap, logWarring)
	}
	l.logRoot = ""
	l.before = 31
	if nil != configInfo {
		if v, ok := configInfo["logger.rootpath"]; ok && v != nil {
			l.logRoot = fmt.Sprintf("%v", v)
		}
		if v, ok := configInfo["logger.logdate"]; ok && v != nil {
			op, ok := v.(int)
			if ok {
				l.before = op
			}
		}
	}

}

func (l *MyLogger) writeLog(level, info string) {
	if _, ok := l.logLevelMap[level]; ok != true {
		return
	}
	logEntry := new(logStruct)
	logEntry.logLevel = level
	logEntry.logValue = info
	l.mutex.Lock()
	instance.catch.PushBack(logEntry)
	l.mutex.Unlock()

}

func (l *MyLogger) write() {
	baseDir := l.getLogDir()
	dir, err := l.createLogDir(baseDir)
	if dir == false {
		l.show(logError, fmt.Sprintf("%v", err))
		return
	}
	for {
		if l.catch.Len() > 0 {
			logEntry := l.catch.Front().Value.(*logStruct)
			l.mutex.Lock()
			l.catch.Remove(l.catch.Front())
			l.mutex.Unlock()
			logPath := fmt.Sprintf("%s%s_%v.logger", baseDir, logEntry.logLevel, time.Now().Format("2006-01-02_15"))
			str := fmt.Sprintf("[%v][%v]%v\n", util.GetNow(), logEntry.logLevel, logEntry.logValue)
			e := writeFile(str, logPath)
			if e != nil {
				s := fmt.Sprintf("写日志失败,错误信息:%v\n\t==>日志内容:%v", err, str)
				l.show(logError, s)
			}
			l.show(logEntry.logLevel, logEntry.logValue)
		} else {
			time.Sleep(1 * time.Second)
		}
	}
}

func (l *MyLogger) show(level string, info string) {
	if l.showLog {
		info = fmt.Sprintf("[%v]%v", level, info)
		level := strings.ToUpper(level)
		if level == strings.ToUpper(logError) {
			logColorPrint(red, info)
		} else if level == strings.ToUpper(logDebug) {
			logColorPrint(blue, info)
		} else if level == strings.ToUpper(logInfo) {
			logColorPrint(darkGreen, info)
		} else if level == strings.ToUpper(logWarring) {
			logColorPrint(yellow, info)
		} else if l.showOther {
			logColorPrint(violet, info)
		} else if l.showOther2 {
			logColorPrint(white, info)
		} else {
			logColorPrint(white, info)
		}
	}
}

func (l *MyLogger) createLogDir(baseDir string) (bool, error) {
	if ok := util.PathExists(baseDir); ok == false {
		l.mutex.Lock()
		if ok := util.PathExists(baseDir); ok == false {
			err := os.MkdirAll(baseDir, os.ModePerm)
			if err != nil {
				s := fmt.Sprintf("创建文件夹[%v]失败,错误信息:%v", baseDir, err)
				l.show(logError, s)
				l.mutex.Unlock()
				return false, err
			}
		}
		l.mutex.Unlock()
	}
	return true, nil
}

func (l *MyLogger) getLogDir() string {
	baseDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		if l.logRoot != "" {
			if filepath.IsAbs(l.logRoot) {
				baseDir = fmt.Sprintf("%v%vlog%v", l.logRoot, util.Separator(), util.Separator())
			} else {
				baseDir = fmt.Sprintf(".%vlog%v%v%v", util.Separator(), util.Separator(), l.logRoot, util.Separator())
			}
		} else {
			baseDir = fmt.Sprintf(".%vlog%v", util.Separator(), util.Separator())
		}
	} else {
		if l.logRoot != "" {
			if filepath.IsAbs(l.logRoot) {
				baseDir = fmt.Sprintf("%v%vlog%v", l.logRoot, util.Separator(), util.Separator())
			} else {
				baseDir = fmt.Sprintf("%v%vlog%v%v%v", baseDir, util.Separator(), util.Separator(), l.logRoot, util.Separator())
			}
		} else {
			baseDir = fmt.Sprintf("%v%vlog%v", baseDir, util.Separator(), util.Separator())
		}
	}
	return baseDir
}

func (l *MyLogger) AutoDelete() {
	if l.before > 0 {
		adl := new(AutoDeleteLog)
		adl.Start(l.getLogDir(), l.before*24)
	}
}

func writeFile(content, path string) error {
	fd, e := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
	if e != nil {
		return e
	}

	defer func() {
		ex := fd.Close()
		if ex != nil {
			fmt.Printf("close logger file error : %v", ex)
		}
	}()

	buf := []byte(content)
	n, e := fd.Write(buf)

	size := len(buf)
	count := n
	retryTime := 1
	maxRetryTimes := 10
	for size > count {
		if retryTime > maxRetryTimes {
			e = fmt.Errorf("写日志文件失败,重试写入次数已经超过%v", maxRetryTimes)
			break
		}
		time.Sleep(10 * time.Millisecond)
		n, e = fd.Write(buf[count:])
		count = count + n
		retryTime++
	}
	return e
}

// GetStackTrace 获取函数调用堆栈
func GetStackTrace(skip int) string {
	stackTrace := ""
	if skip < 0 {
		skip = 0
	}
	for {
		caller, _, line, ok := runtime.Caller(skip)
		if ok {
			if stackTrace == "" {
				stackTrace = fmt.Sprintf("\t==>%v %v", runtime.FuncForPC(caller).Name(), line)
			} else {
				stackTrace = fmt.Sprintf("%v\n\t==>%v %v", stackTrace, runtime.FuncForPC(caller).Name(), line)
			}
		} else {
			break
		}
		skip++
	}
	return stackTrace
}

func CurFunName() string {
	return getFunctionName(3)
}

func getFunctionName(skip int) string {
	pc := make([]uintptr, 1)
	if skip < 0 {
		skip = 2
	}
	n := runtime.Callers(skip, pc) // 2 表示跳过 getFunctionName 和 getFunctionName 的调用
	if n == 0 {
		return "unknown"
	}
	f := runtime.FuncForPC(pc[0])
	_, l := f.FileLine(pc[0])
	return fmt.Sprintf("%v %v", f.Name(), l)
}
